﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Media;
using System.Windows.Navigation;
using BacteriaSimulation.GUI;
using BacteriaSimulation.Simulation;
using Microsoft.Phone.Controls;
using Microsoft.Phone.Shell;
using MobileBact.Resources;
using System.Threading;
using System.Windows.Shapes;

namespace MobileBact
{
    public partial class MainPage : PhoneApplicationPage
    {
        protected delegate void updateDelegateEventHandler(SimulationUpdateEventArgs args);
        protected delegate void deathDelegateEventHandler(BacteriumUpdateEventArgs args);

        public Simulation Simulation { get; set; }
        public Thread SimulationThread { get; set; }
        public Canvas SimulationCanvas
        {
            get
            { return this.simulationCanvas; }
            set
            { this.simulationCanvas = value; }
        }
        protected Dictionary<Bacterium, BacteriumView> viewDictonary;
        protected List<Line> gridLines;
        protected Boolean gridVisible = false;

        protected SimulationEnvironment Env { get; set; }
        protected Point ManipulationStartPosition { get; set; }

        // Constructor
        public MainPage()
        {
            InitializeComponent();

            cyanoCountLabel.Foreground = new SolidColorBrush(Colors.Green);
            cannibalCountLabel.Foreground = new SolidColorBrush(Colors.Red);

            // Sample code to localize the ApplicationBar
            BuildLocalizedApplicationBar();

            Settings settings = (App.Current as App).LastSettings;

            SimulationEnvironment env = new SimulationEnvironment(this.simulationCanvas.Width, this.simulationCanvas.Height, settings.InitialEnvironmentResources, 0.2, true);
            this.Env = env;
            this.Simulation = new Simulation(env, 25, 15);
            this.viewDictonary = new Dictionary<Bacterium, BacteriumView>();
            this.gridLines = new List<Line>();
            //this.addCyanoButton.Click += addCyanoButton_Click;

            System.Threading.Thread thread = new System.Threading.Thread(Simulation.SimulationThreadStart);
            SimulationThread = thread;
            Simulation.SimulationUpdate += Simulation_SimulationUpdate;
            thread.Start();
            simulationCanvas.Tap += simulationCanvas_Tap;
            simulationCanvas.ManipulationStarted += simulationCanvas_ManipulationStarted;
            simulationCanvas.ManipulationDelta += simulationCanvas_ManipulationDelta;
        }

        void simulationCanvas_ManipulationStarted(object sender, System.Windows.Input.ManipulationStartedEventArgs e)
        {
            this.ManipulationStartPosition = e.ManipulationOrigin;
        }

        void simulationCanvas_ManipulationDelta(object sender, System.Windows.Input.ManipulationDeltaEventArgs e)
        {
            Double x = ManipulationStartPosition.X + e.CumulativeManipulation.Translation.X;
            Double y = ManipulationStartPosition.Y + e.CumulativeManipulation.Translation.Y;


            //if (e.DeltaManipulation.Translation.X == 0 && y == e.DeltaManipulation.Translation.Y)
            //{
            //    System.Diagnostics.Debug.WriteLine("Translation: [0 ,0]");
            //}

            Settings settings = (App.Current as App).LastSettings;
            BacteriumSimulationStrategy strategy = null;
            Brush colorBrush = null;
            double initialEnergy = 0;
            double maxEnergy = 0;
            double energyIncrement = 0.2;
            double initialSpeedMagnitude = 0.0;
            double acceleration = 0.0;
            if (settings.SelectedBacteriaType == "Cyano")
            {
                strategy = new CyanobacteriumSimulationStrategy();
                colorBrush = new SolidColorBrush(Colors.Green);
                initialEnergy = 29;
                maxEnergy = 30;
                energyIncrement = 0.2;

                initialSpeedMagnitude = settings.InitialSpeedMagnitude;
                acceleration = settings.Acceleration;
            }
            else if (settings.SelectedBacteriaType == "Fago")
            {
                strategy = new CannibalBacteriumStrategy();
                colorBrush = new SolidColorBrush(Colors.Red);
                initialEnergy = 90;
                maxEnergy = 90;
                energyIncrement = 0.2;

                initialSpeedMagnitude = settings.FagoInitialSpeedMagnitute;
                acceleration = settings.FagoAcceleration;
            }
            else if (settings.SelectedBacteriaType == "Guardian")
            {
                strategy = new GuardianSimulationStrategy();
                colorBrush = new SolidColorBrush(Colors.Blue);
                initialEnergy = 90;
                maxEnergy = 90;
                energyIncrement = 0.2;

                initialSpeedMagnitude = settings.FagoInitialSpeedMagnitute;
                acceleration = settings.FagoAcceleration;
            }

            Bacterium bact = new Bacterium(this.Simulation, new SimulationPoint(x, y), Vector2D.GetRandomVector2D(1),
                initialSpeedMagnitude, acceleration, initialEnergy, strategy, maxEnergy, energyIncrement);
            SimulationCommand cmd = new AddCommand(bact);
            AddBacteriumView(bact, colorBrush);
            Simulation.EnqueueCommand(cmd);
            bact.Death += bact_Death;
        }

        void RestartSimulation()
        {
            try
            {
                if (Simulation != null)
                {
                    Simulation.EnqueueCommand(new SimulationCommand("stop"));
                    try
                    {
                        Monitor.Enter(Simulation.UpdateSyncObject);
                        Monitor.PulseAll(Simulation.UpdateSyncObject);
                    }
                    finally
                    {
                        Monitor.Exit(Simulation.UpdateSyncObject);
                    }
                    clearCanvasAsync();
                }

                Settings settings = (App.Current as App).LastSettings;
                SimulationEnvironment env = new SimulationEnvironment(this.simulationCanvas.Width, this.simulationCanvas.Height, settings.InitialEnvironmentResources, 0.2, true);
                this.Env = env;
                this.Simulation = new Simulation(env, 25, 15);
                this.viewDictonary = new Dictionary<Bacterium, BacteriumView>();

                System.Threading.Thread thread = new System.Threading.Thread(Simulation.SimulationThreadStart);
                SimulationThread = thread;
                Simulation.SimulationUpdate += Simulation_SimulationUpdate;
                thread.Start();
            }
            catch (Exception ex)
            {
                System.Diagnostics.Debug.WriteLine("Error: " + ex.Message);
                clearCanvas();
            }
        }

        private void clearCanvas()
        {
            this.SimulationCanvas.Children.Clear();
            //this.viewDictonary.Clear();
        }

        void clearCanvasAsync()
        {
            Action clearAction = new Action(clearCanvas);
            Dispatcher.BeginInvoke(clearAction);
        }

        void addCyanoButton_Click(object sender, EventArgs e)
        {
            Settings settings = (App.Current as App).LastSettings;

            Bacterium bact = new Bacterium(this.Simulation, new SimulationPoint(0, 0), Vector2D.GetRandomVector2D(1),
                settings.InitialSpeedMagnitude, settings.Acceleration, 29, new WallDirectedBacteriumStrategy(), 30, 0.2);
            SimulationCommand cmd = new AddCommand(bact);
            AddBacteriumView(bact, null);
            Simulation.EnqueueCommand(cmd);
            bact.Death += bact_Death;
        }

        void simulationCanvas_Tap(object sender, System.Windows.Input.GestureEventArgs e)
        {
            try
            {
                Settings settings = (App.Current as App).LastSettings;
                Point position = e.GetPosition(simulationCanvas);

                Bacterium bact = new Bacterium(this.Simulation, new SimulationPoint(position.X, position.Y), Vector2D.GetRandomVector2D(1), 30, 
                    settings.FagoAcceleration, 90.0, new GuardianSimulationStrategy(), 90, 0.2);
                SimulationCommand cmd = new AddCommand(bact);
                AddBacteriumView(bact, new SolidColorBrush(Colors.Blue));
                Simulation.EnqueueCommand(cmd);
            }
            catch (Exception ex)
            {
                MessageBox.Show(ex.StackTrace, ex.Message, MessageBoxButton.OK);
            }
        }

        protected BacteriumView AddBacteriumView(Bacterium bacterium, Brush fill)
        {
            BacteriumView view = new BacteriumView();
            if (fill != null)
            {
                view.internalEllipse.Fill = fill;
                view.internalEllipse.Stroke = fill;
            }
            view.Width = bacterium.Width;
            view.Height = bacterium.Height;
            view.AdjustSize(bacterium.Width, bacterium.Height);
            this.SimulationCanvas.Children.Add(view);
            Canvas.SetLeft(view, bacterium.X);
            Canvas.SetTop(view, bacterium.Y);
            viewDictonary.Add(bacterium, view);

            return view;
        }

        void Simulation_SimulationUpdate(SimulationUpdateEventArgs args)
        {
            //if (guiState == SimulationState.STOPPED)
            //{
            //    return;
            //}
            Object[] functionArgs = new Object[1];
            functionArgs[0] = args;
            updateDelegateEventHandler updateDelegate = UpdateView;

            // "begin" is used to calculate how long it takes to repaint the canvas.
            DateTime begin = DateTime.Now;

            this.Dispatcher.BeginInvoke(updateDelegate, functionArgs);
            lock (Simulation.UpdateSyncObject)
            {
                Monitor.Wait(Simulation.UpdateSyncObject);
            }
            //if (guiState == SimulationState.STOPPED)
            //{
            //    clearCanvas();
            //}
            DateTime end = DateTime.Now;
            TimeSpan howLong = end.Subtract(begin);
            TimeSpan simulationInterval = new TimeSpan(0, 0, 0, 0, 4);
            if (howLong.CompareTo(simulationInterval) == -1)
            {
                System.Threading.Thread.Sleep(simulationInterval.Subtract(howLong));
            }

        }

        public void UpdateView(SimulationUpdateEventArgs args)
        {
            try
            {
                System.Threading.Monitor.Enter(Simulation.UpdateSyncObject);
                foreach (Bacterium b in args.Bacteria)
                {
                    UpdateBacteriumView(b);
                }
                this.cyanoCountLabel.Text = args.CyanobacteriaCount.ToString();
                this.cannibalCountLabel.Text = args.CannibalBacteriaCount.ToString();
                this.availableResourcesLabel.Text = args.AvalaibleResources.ToString();
            }
            catch (InvalidOperationException iex)
            {
                System.Diagnostics.Debug.WriteLine(iex.StackTrace);
            }
            finally
            {
                Monitor.PulseAll(Simulation.UpdateSyncObject);
                Monitor.Exit(Simulation.UpdateSyncObject);
                
            }
        }

        protected void UpdateBacteriumView(Bacterium bacterium)
        {
            try
            {
                BacteriumView view = null;

                if (!this.viewDictonary.ContainsKey(bacterium))
                {
                    if (bacterium.State != BacteriumState.DEAD)
                    {
                        Brush fill = null;
                        if (bacterium.Diet == DietType.CARNIVORE)
                        {
                            fill = new SolidColorBrush(Colors.Red);
                        }
                        view = AddBacteriumView(bacterium, fill);
                        bacterium.Death += bact_Death;
                    }
                    else
                    {
                        return;
                    }
                }
                else
                {
                    if (bacterium.State == BacteriumState.DEAD)
                    {
                        RemoveBacteriumView(bacterium);
                        return;
                    }
                    else
                    {
                        view = this.viewDictonary[bacterium];
                    }
                }

                view.AdjustSize(bacterium.Width, bacterium.Height);
#if !DEBUG
                if (bacterium.X == double.NaN || bacterium.Y == double.NaN)
                {
                    bacterium.State = BacteriumState.DEAD;
                    bacterium.X = -140;
                    bacterium.Y = -140;
                }  
#endif
                Canvas.SetLeft(view, bacterium.X);
                Canvas.SetTop(view, bacterium.Y);
            }
            catch (Exception ex)
            {
                System.Diagnostics.Debug.WriteLine(ex.Message + " -- " + ex.StackTrace);
#if DEBUG
                throw;
#endif
            }
        }

        protected void RemoveBacteriumView(Bacterium bact)
        {
            if (viewDictonary.ContainsKey(bact))
            {
                BacteriumView view = viewDictonary[bact];
                SimulationCanvas.Children.Remove(view);
                viewDictonary.Remove(bact);
            }
        }

        void bact_Death(BacteriumUpdateEventArgs args)
        {
            Object[] functionArgs = new Object[1];
            functionArgs[0] = args;
            deathDelegateEventHandler d = BacteriumDeath;
            this.Dispatcher.BeginInvoke(d, functionArgs);
        }

        protected void BacteriumDeath(BacteriumUpdateEventArgs args)
        {
            if (viewDictonary.ContainsKey(args.SourceBacterium))
            {
                BacteriumView view = viewDictonary[args.SourceBacterium];
                this.viewDictonary.Remove(args.SourceBacterium);
                this.SimulationCanvas.Children.Remove(view);
            }
        }

        protected void DrawGrid()
        {
            ClearGrid();
            this.gridLines.Clear();

            double cellWidth = Env.Width / Simulation.Grid.ColumnCount;
            double cellHeight = Env.Height / Simulation.Grid.RowCount;
            // Draws vertical lines
            double currentX = cellWidth;
            while (currentX <= Env.Width)
            {
                Line vertical = new Line();
                vertical.Stroke = new SolidColorBrush(Colors.Black); // Brushes.Black;
                vertical.X1 = currentX;
                vertical.X2 = currentX;
                vertical.Y1 = 0;
                vertical.Y2 = Env.Height;
                SimulationCanvas.Children.Add(vertical);
                this.gridLines.Add(vertical);

                currentX += cellWidth;
            }
            // Draws horizontal lines
            double currentY = cellHeight;
            while (currentY <= Env.Height)
            {
                Line horizontal = new Line();
                horizontal.Stroke = new SolidColorBrush(Colors.Black);
                horizontal.X1 = 0;
                horizontal.X2 = Env.Width;
                horizontal.Y1 = currentY;
                horizontal.Y2 = currentY;
                SimulationCanvas.Children.Add(horizontal);
                this.gridLines.Add(horizontal);

                currentY += cellHeight;
            }

            this.gridVisible = true;
        }

        protected void ClearGrid()
        {
            foreach (Line line in gridLines)
            {
                SimulationCanvas.Children.Remove(line);
            }
            gridLines.Clear();

            this.gridVisible = false;
        }

         //Sample code for building a localized ApplicationBar
        private void BuildLocalizedApplicationBar()
        {
            // Set the page's ApplicationBar to a new instance of ApplicationBar.
            ApplicationBar = new ApplicationBar();

            // Buttons -- //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
            //Create a new button and set the text value to the localized string from AppResources.
            ApplicationBarIconButton addCyanoButton = new ApplicationBarIconButton(new Uri("/Assets/AppBar/appbar.add.rest.png", UriKind.Relative));
            addCyanoButton.Text = AppResources.AppBarButtonText;
            addCyanoButton.Click += addCyanoButton_Click;
            ApplicationBar.Buttons.Add(addCyanoButton);

            ApplicationBarIconButton addGuardianButton = new ApplicationBarIconButton(new Uri("/Assets/AppBar/appbar.add.rest.png", UriKind.Relative));
            addGuardianButton.Text = AppResources.AddGuardianButtonText;
            addGuardianButton.Click += addGuardianButton_Click;
            ApplicationBar.Buttons.Add(addGuardianButton);

            ApplicationBarIconButton showLinesButton = new ApplicationBarIconButton(new Uri("/Assets/AppBar/showLines.png", UriKind.Relative));
            showLinesButton.Text = AppResources.ShowGridMenuItemText;
            showLinesButton.Click += showLinesButton_Click;
            ApplicationBar.Buttons.Add(showLinesButton);
            //  //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

            // Create a new menu item with the localized string from AppResources.
            ApplicationBarMenuItem addCyanoItem = new ApplicationBarMenuItem(AppResources.AddCynoMenuItemText);
            ApplicationBar.MenuItems.Add(addCyanoItem);
            addCyanoItem.Click += addCyanoItem_Click;

            ApplicationBarMenuItem addFagoItem = new ApplicationBarMenuItem(AppResources.AddFagoItemText);
            ApplicationBar.MenuItems.Add(addFagoItem);
            addFagoItem.Click += addFagoItem_Click;

            ApplicationBarMenuItem settingsItem = new ApplicationBarMenuItem(AppResources.SettingsMenuItemText);
            ApplicationBar.MenuItems.Add(settingsItem);
            settingsItem.Click += settingsItem_Click;

            ApplicationBarMenuItem restartItem = new ApplicationBarMenuItem(AppResources.RestartMenuItemText);
            ApplicationBar.MenuItems.Add(restartItem);
            restartItem.Click += restartItem_Click;
        }

        void addGuardianButton_Click(object sender, EventArgs e)
        {
            Settings settings = (App.Current as App).LastSettings;
            settings.SelectedBacteriaType = "Guardian";
        }

        void addCyanoItem_Click(object sender, EventArgs e)
        {
            Settings settings = (App.Current as App).LastSettings;
            settings.SelectedBacteriaType = "Cyano";
        }

        void addFagoItem_Click(object sender, EventArgs e)
        {
            Settings settings = (App.Current as App).LastSettings;
            settings.SelectedBacteriaType = "Fago";
        }

        void showLinesButton_Click(object sender, EventArgs e)
        {
            if (gridVisible)
            {
                ClearGrid();
            }
            else
            {
                this.DrawGrid();
            }
        }

        void restartItem_Click(object sender, EventArgs e)
        {
            RestartSimulation();
        }

        void settingsItem_Click(object sender, EventArgs e)
        {
            NavigationService.Navigate(new Uri("/SettingsPage.xaml", UriKind.Relative));
        }

        protected override void OnNavigatingFrom(NavigatingCancelEventArgs e)
        {
            base.OnNavigatingFrom(e);
            Simulation.EnqueueCommand(new SimulationCommand("pause"));
        }

        protected override void OnNavigatedTo(NavigationEventArgs e)
        {
            base.OnNavigatedTo(e);
            this.Simulation.EnqueueCommand(new SimulationCommand("resume"));
        }
    }
}